﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using iNet.Tasks;

namespace iNet.Media.Video
{
	/// <summary>
	/// 表示一個時間上已裁切的影片。
	/// </summary>
	public class TrimmedVideo : MediaBase, IVideo
	{
		#region 欄位
		long? _ActualEndPosition;
		long? _ActualStartPosition;
		long? _Duration;
		readonly long? _EndPosition;
		PlayableMediaState _MediaState = PlayableMediaState.Stopped;
		long? _Position;
		readonly IVideo _SourceVideo;
		readonly long? _StartPosition;
		readonly object _SyncRoot = new object();
		readonly TaskExecutor _TaskExecutor = new TaskExecutor(true, 1);
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 TrimmedVideo 執行個體。
		/// </summary>
		/// <param name="source">原始影片。</param>
		/// <param name="startPosition">起始播放位置，單位為毫秒。Null (Visual Basic 為 Nothing) 表示從頭播放。</param>
		/// <param name="endPosition">結束播放位置，單位為毫秒。Null (Visual Basic 為 Nothing) 表示播放至結尾。</param>
		/// <param name="isReadOnly">是否為唯讀。</param>
		/// <exception cref="ArgumentException">來源影片不支援搜尋 (CanSeek 屬性值為 False)。</exception>
		public TrimmedVideo(IVideo source, long? startPosition, long? endPosition, bool isReadOnly)
			: base(source.MediaStorage, isReadOnly, (source.Metadata != null ? (isReadOnly ? source.Metadata.GetAsReadOnly() : source.Metadata.Clone() as IMetadata) : null))
		{
			//確認來源影片
			if (!source.CanSeek)
				throw new ArgumentException();

			//計算裁切範圍
			_SourceVideo = (source.Clone() as IVideo);
			_StartPosition = startPosition;
			_EndPosition = endPosition;
			this.UpdateDuration(_SourceVideo.Duration);

			//事件連結
			_SourceVideo.DurationChanged += this.OnSourceDurationChanged;
			_SourceVideo.PlayablePositionChanged += this.OnSourcePlayablePositionChanged;
			_SourceVideo.PositionChanged += this.OnSourcePositionChanged;
		}
		private TrimmedVideo(TrimmedVideo video)
			: base(video.MediaStorage, false, (video.Metadata != null ? video.Metadata.Clone() as IMetadata : null))
		{
			//複製屬性
			_SourceVideo = (video._SourceVideo.Clone() as IVideo);
			_StartPosition = video._StartPosition;
			_EndPosition = video._EndPosition;
			_Duration = video._Duration;

			//事件連結
			_SourceVideo.DurationChanged += this.OnSourceDurationChanged;
			_SourceVideo.PlayablePositionChanged += this.OnSourcePlayablePositionChanged;
			_SourceVideo.PositionChanged += this.OnSourcePositionChanged;
		}
		#endregion
		#region ActualEndPosition
		internal long? ActualEndPosition
		{
			get
			{
				return _ActualEndPosition;
			}
		}
		#endregion
		#region ActualStartPosition
		internal long? ActualStartPosition
		{
			get
			{
				return _ActualStartPosition;
			}
		}
		#endregion
		#region Balance
		/// <summary>
		/// 取得或設定左右喇叭的音量平衡，預設值為 0。
		/// </summary>
		public double Balance
		{
			get
			{
				return _SourceVideo.Balance;
			}
			set
			{
				_SourceVideo.Balance = value;
			}
		}
		#endregion
		#region CanPause
		/// <summary>
		/// 取得媒體是否支援暫停。
		/// </summary>
		public bool CanPause
		{
			get
			{
				return _SourceVideo.CanPause;
			}
		}
		#endregion
		#region CanSeek
		/// <summary>
		/// 取得媒體是否支援搜尋。
		/// </summary>
		public bool CanSeek
		{
			get
			{
				return _SourceVideo.CanSeek;
			}
		}
		#endregion
		#region ChangeMediaState
		void ChangeMediaState(PlayableMediaState state)
		{
			PlayableMediaState oldState;
			lock (this)
			{
				if (_MediaState == state)
					return;
				oldState = _MediaState;
				_MediaState = state;
			}
			this.MediaStateChanged.RaiseEvent(this, new ValueChangedEventArgs<PlayableMediaState>(oldState, state));
		}
		#endregion
		#region Clone
		/// <summary>
		/// 建立可修改的影片複本。
		/// </summary>
		/// <returns>影片的複本。</returns>
		public override MediaBase Clone()
		{
			return new TrimmedVideo(this);
		}
		#endregion
		#region Dispose
		/// <summary>
		/// 釋放媒體所使用的資源。
		/// </summary>
		/// <param name="disposing">True 表示需釋放 Managed 及 Unmanaged 資源，False 表示僅需釋放 Unmanaged 資源。</param>
		protected override void Dispose(bool disposing)
		{
			//移除事件連結
			_SourceVideo.DurationChanged -= this.OnSourceDurationChanged;
			_SourceVideo.PlayablePositionChanged -= this.OnSourcePlayablePositionChanged;
			_SourceVideo.PositionChanged -= this.OnSourcePositionChanged;

			//釋放來源影片
			_TaskExecutor.CancelAllTasks();
			_TaskExecutor.InvokeAsync(() =>
			{
				_SourceVideo.Dispose();
				_TaskExecutor.Dispose();
			});

			//呼叫基底
			base.Dispose(disposing);
		}
		#endregion
		#region Duration
		/// <summary>
		/// 取得媒體的時間長度，單位為毫秒。Null (Visual Basic 為 Nothing) 表示媒體沒有固定的時間長度。
		/// </summary>
		public long? Duration
		{
			get
			{
				return _Duration;
			}
		}
		#endregion
		#region DurationChanged
		/// <summary>
		/// 發生在 Duration 屬性值改變後。
		/// </summary>
		public event EventHandler<DataEventArgs<long?>> DurationChanged;
		#endregion
		#region GetAsReadOnly
		/// <summary>
		/// 取得唯讀版本的媒體。
		/// </summary>
		/// <returns>唯讀版本的媒體。</returns>
		public override IMedia GetAsReadOnly()
		{
			if (this.IsReadOnly)
				return this;
			return new TrimmedVideo(this);
		}
		#endregion
		#region HasAudio
		/// <summary>
		/// 取得影片中是否包含音訊。
		/// </summary>
		public bool HasAudio
		{
			get
			{
				return _SourceVideo.HasAudio;
			}
		}
		#endregion
		#region HasError
		/// <summary>
		/// 取得媒體播放時是否發生錯誤。
		/// </summary>
		public bool HasError
		{
			get
			{
				return (_MediaState == PlayableMediaState.Error);
			}
		}
		#endregion
		#region HasVideo
		/// <summary>
		/// 取得影片中是否包含視訊。
		/// </summary>
		public bool HasVideo
		{
			get
			{
				return _SourceVideo.HasVideo;
			}
		}
		#endregion
		#region IsMuted
		/// <summary>
		/// 取得或設定是否為靜音模式。
		/// </summary>
		public bool IsMuted
		{
			get
			{
				return _SourceVideo.IsMuted;
			}
			set
			{
				_SourceVideo.IsMuted = value;
			}
		}
		#endregion
		#region MediaState
		/// <summary>
		/// 取得媒體的播放狀態。
		/// </summary>
		public PlayableMediaState MediaState
		{
			get
			{
				return _MediaState;
			}
		}
		#endregion
		#region MediaStateChanged
		/// <summary>
		/// 發生在 MediaState 屬性值改變後。
		/// </summary>
		public event EventHandler<ValueChangedEventArgs<PlayableMediaState>> MediaStateChanged;
		#endregion
		#region OnSourceDurationChanged
		void OnSourceDurationChanged(object sender, DataEventArgs<long?> e)
		{
			this.UpdateDuration(e.Data);
		}
		#endregion
		#region OnSourcePlayablePositionChanged
		void OnSourcePlayablePositionChanged(object sender, EventArgs e)
		{
			this.PlayablePositionChanged.RaiseEvent(this, EventArgs.Empty);
		}
		#endregion
		#region OnSourcePositionChanged
		void OnSourcePositionChanged(object sender, DataEventArgs<long?> e)
		{
			_Position = null;
			if (e.Data < _ActualStartPosition)
			{
				try
				{
					_SourceVideo.Position = _ActualStartPosition;
				}
				catch
				{
					this.ChangeMediaState(PlayableMediaState.Error);
					return;
				}
			}
			if (e.Data >= _ActualEndPosition)
			{
				this.ChangeMediaState(PlayableMediaState.Stopped);
				_TaskExecutor.InvokeAsync(() =>
				{
					try
					{
						_SourceVideo.Stop();
					}
					catch
					{
						this.ChangeMediaState(PlayableMediaState.Error);
						return;
					}
				});
			}
			this.PositionChanged.RaiseEvent(this, new DataEventArgs<long?>(this.Position));
		}
		#endregion
		#region Pause
		/// <summary>
		/// 暫停媒體播放。
		/// </summary>
		public void Pause()
		{
			if (_MediaState == PlayableMediaState.Playing)
			{
				this.ChangeMediaState(PlayableMediaState.Paused);
				_TaskExecutor.InvokeAsync(() =>
				{
					try
					{
						_SourceVideo.Pause();
					}
					catch
					{
						this.ChangeMediaState(PlayableMediaState.Error);
					}
				});
			}
		}
		#endregion
		#region Play
		/// <summary>
		/// 開始播放媒體。
		/// </summary>
		public void Play()
		{
			if (_MediaState == PlayableMediaState.Paused || _MediaState == PlayableMediaState.Stopped)
			{
				try
				{
					if (_Duration != null && this.Position < _Duration)
					{
						bool setupPosition = (_MediaState == PlayableMediaState.Stopped);
						this.ChangeMediaState(PlayableMediaState.Playing);
						_TaskExecutor.InvokeAsync(() =>
						{
							try
							{
								if (setupPosition)
								{
									long? position = _Position;
									if (position != null)
									{
										if (_ActualStartPosition != null)
											_SourceVideo.Position = (_ActualStartPosition + position);
										else
											_SourceVideo.Position = position;
									}
								}
								_SourceVideo.Play();
							}
							catch
							{
								this.ChangeMediaState(PlayableMediaState.Error);
							}
						});
					}
					else
					{
						this.ChangeMediaState(PlayableMediaState.Stopped);
						_TaskExecutor.InvokeAsync(() =>
						{
							try
							{
								_SourceVideo.Stop();
							}
							catch
							{
								this.ChangeMediaState(PlayableMediaState.Error);
							}
						});
					}
				}
				catch
				{
					this.ChangeMediaState(PlayableMediaState.Error);
				}
			}
		}
		#endregion
		#region PlayablePositionChanged
		/// <summary>
		/// 發生在 PlayablePositionStart 或 PlayablePositionEnd 屬性值改變後。
		/// </summary>
		public event EventHandler PlayablePositionChanged;
		#endregion
		#region PlayablePositionEnd
		/// <summary>
		/// 取得可播放的結束位置，單位為毫秒。Null (Visual Basic 為 Nothing) 表示媒體沒有明確的可播放位置。
		/// </summary>
		public long? PlayablePositionEnd
		{
			get
			{
				long? sourcePosition = _SourceVideo.PlayablePositionEnd;
				if (sourcePosition != null && _ActualStartPosition != null)
					sourcePosition -= _ActualStartPosition;
				return sourcePosition;
			}
		}
		#endregion
		#region PlayablePositionStart
		/// <summary>
		/// 取得可播放的起始位置，單位為毫秒。Null (Visual Basic 為 Nothing) 表示媒體沒有明確的可播放位置。
		/// </summary>
		public long? PlayablePositionStart
		{
			get
			{
				long? sourcePosition = _SourceVideo.PlayablePositionStart;
				if (sourcePosition != null && _ActualStartPosition != null)
					sourcePosition -= _ActualStartPosition;
				return sourcePosition;
			}
		}
		#endregion
		#region Position
		/// <summary>
		/// 取得或設定媒體目前的播放位置，單位為毫秒。Null (Visual Basic 為 Nothing) 表示媒體沒有明確的播放位置。
		/// </summary>
		public long? Position
		{
			get
			{
				long? position = _Position;
				if (position == null)
				{
					position = _SourceVideo.Position;
					if (position != null)
					{
						if (position < _ActualStartPosition)
							_Position = 0;
						else if (position > _ActualEndPosition)
							_Position = _Duration;
						else if (_ActualStartPosition != null)
							_Position = (position.Value - _ActualStartPosition.Value);
					}
				}
				return _Position;
			}
			set
			{
				if (value != null)
				{
					if (value < _ActualStartPosition)
						value = _ActualStartPosition;
					else if (value > _ActualEndPosition)
						value = _ActualEndPosition;
					_SourceVideo.Position = value;
				}
				else
					_SourceVideo.Position = null;
				_Position = value;
			}
		}
		#endregion
		#region PositionChanged
		/// <summary>
		/// 發生在 Position 屬性值改變後。
		/// </summary>
		public event EventHandler<DataEventArgs<long?>> PositionChanged;
		#endregion
		#region RawAudioFormat
		/// <summary>
		/// 取得原始音訊格式。
		/// </summary>
		public IMediaFormat RawAudioFormat
		{
			get
			{
				return _SourceVideo.RawAudioFormat;
			}
		}
		#endregion
		#region RawContainerFormat
		/// <summary>
		/// 取得原始影音容器格式。
		/// </summary>
		public IMediaFormat RawContainerFormat
		{
			get
			{
				return _SourceVideo.RawContainerFormat;
			}
		}
		#endregion
		#region RawVideoFormat
		/// <summary>
		/// 取得原始視訊格式。
		/// </summary>
		public IMediaFormat RawVideoFormat
		{
			get
			{
				return _SourceVideo.RawVideoFormat;
			}
		}
		#endregion
		#region Stop
		/// <summary>
		/// 停止媒體播放。
		/// </summary>
		public void Stop()
		{
			this.ChangeMediaState(PlayableMediaState.Stopped);
			_TaskExecutor.InvokeAsync(() =>
			{
				try
				{
					_SourceVideo.Stop();
				}
				catch
				{
					this.ChangeMediaState(PlayableMediaState.Error);
				}
			});
		}
		#endregion
		#region UpdateDuration
		void UpdateDuration(long? sourceDuration)
		{
			//重新計算時間長度
			if (_StartPosition != null || _EndPosition != null)
			{
				long? newDuration;
				if (sourceDuration == null || sourceDuration <= 0)
				{
					_ActualStartPosition = null;
					_ActualEndPosition = null;
					newDuration = null;
				}
				else
				{
					//計算裁切範圍
					if (_StartPosition != null)
					{
						if (_StartPosition < 0)
							_ActualStartPosition = 0;
						else if (_StartPosition > sourceDuration)
							_ActualStartPosition = sourceDuration;
						else
							_ActualStartPosition = _StartPosition;
					}
					if (_EndPosition != null)
					{
						if (_EndPosition > sourceDuration)
							_ActualEndPosition = sourceDuration;
						else if (_EndPosition < _StartPosition)
							_ActualEndPosition = _StartPosition;
						else
							_ActualEndPosition = _EndPosition;
					}
					newDuration = sourceDuration;
					if (_ActualEndPosition != null)
						newDuration = _ActualEndPosition;
					if (_ActualStartPosition != null)
						newDuration = (newDuration.Value - _ActualStartPosition.Value);
					if (newDuration < 0)
						newDuration = 0;
					if (newDuration == _Duration)
						return;
					_Duration = newDuration;

					//變更影片播放位置
					if (_MediaState != PlayableMediaState.Error)
					{
						try
						{
							long? position = _SourceVideo.Position;
							if (position < _ActualStartPosition)
								_SourceVideo.Position = _ActualStartPosition;
							else if (position > _ActualEndPosition)
								_SourceVideo.Position = _ActualEndPosition;
						}
						catch
						{
							this.ChangeMediaState(PlayableMediaState.Error);
						}
					}
				}
			}
			else
				_Duration = sourceDuration;

			//引發事件
			if (this.DurationChanged != null)
				this.DurationChanged(this, new DataEventArgs<long?>(_Duration));
		}
		#endregion
		#region VideoOutputWindowHandle
		/// <summary>
		/// 取得或設定視訊輸出的視窗識別碼 (Window Handle)。
		/// </summary>
		public IntPtr VideoOutputWindowHandle
		{
			get
			{
				return _SourceVideo.VideoOutputWindowHandle;
			}
			set
			{
				_SourceVideo.VideoOutputWindowHandle = value;
			}
		}
		#endregion
		#region VideoPixelHeight
		/// <summary>
		/// 取得視訊的像素高度。
		/// </summary>
		public int VideoPixelHeight
		{
			get
			{
				return _SourceVideo.VideoPixelHeight;
			}
		}
		#endregion
		#region VideoPixelWidth
		/// <summary>
		/// 取得視訊的像素寬度。
		/// </summary>
		public int VideoPixelWidth
		{
			get
			{
				return _SourceVideo.VideoPixelWidth;
			}
		}
		#endregion
		#region Volume
		/// <summary>
		/// 取得或設定音量大小，範圍為 0 ~ 1，預設值為 0.5。
		/// </summary>
		public double Volume
		{
			get
			{
				return _SourceVideo.Volume;
			}
			set
			{
				_SourceVideo.Volume = value;
			}
		}
		#endregion
	}
}
